package com.cloud.mybatis.config;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.cloud.mybatis.domain.OrderColumn;
import com.cloud.mybatis.domain.PageModelNew;
import com.cloud.mybatis.domain.SortRuleEnum;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;

import java.sql.Connection;
import java.util.List;

/**
 * 自定义排序
 * 原文链接：https://blog.csdn.net/qq_33803102/article/details/135831609
 */
@Slf4j
public class MybatisPageOrderSqlInterceptor extends JsqlParserSupport implements InnerInterceptor {

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {

        if (log.isDebugEnabled()) {
            log.info(">>>>> 自定义排序拦截 StatementHandler[{}]", sh);
            log.info(">>>>> 自定义排序拦截 connection[{}]", connection);
            log.info(">>>>> 自定义排序拦截 transactionTimeout[{}]", transactionTimeout);
        }

        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct != SqlCommandType.SELECT) {
            // 不是查询操作则直接跳过，不做任何操作
            return;
        }

        PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
        // 取分页查询的入参 PageDTO
        PageModelNew pageDto = getPageDTO(mpSh);
        if (pageDto == null || CollUtil.isEmpty(pageDto.getOrderColumns())) {
            // 不是自定义分页查询，不做处理
            return;
        }
        mpBs.sql(parserMulti(mpBs.sql(), pageDto));
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        PageModelNew dto;
        if (obj instanceof PageModelNew o) {
            dto = o;
        } else {
            // 如果不是分页查询，则不做处理
            return;
        }

        // 1. 解析SQL
        PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
        List<OrderByElement> originOrderList = plainSelect.getOrderByElements();
        List<SelectItem> selectItems = plainSelect.getSelectItems();
        if ("COUNT".equals(selectItems.get(0).getASTNode().jjtGetFirstToken().toString())){
            //如果是count查询不处理
            return;
        }
        List<OrderByElement> clonedOriginOrder = null;
        if (CollUtil.isNotEmpty(originOrderList)) {
            // 创建副本，然后删除原有排序规则
            clonedOriginOrder = BeanUtil.copyToList(originOrderList, OrderByElement.class);
            originOrderList.clear();
        }

        // 2. 向 plainSelect 中添加自定义排序规则
        addCustomizeSortColumns(dto, plainSelect);

        // 3. 添加SQL的原始排序规则 - 追加到末尾
        if (CollUtil.isNotEmpty(clonedOriginOrder)) {
            plainSelect.addOrderByElements(clonedOriginOrder);
        }

        if (log.isDebugEnabled()) {
            log.info("<<<<< 自定义排序拦截 plainSelect[{}]", plainSelect);
        }
    }

    /**
     * 添加自定义排序规则
     *
     * @param dto SQL查询入参
     * @param plainSelect 明文
     * @author weiheng
     **/
    private void addCustomizeSortColumns(PageModelNew dto, PlainSelect plainSelect) {

        // 组装排序
        List<OrderColumn> orderColumns = dto.getOrderColumns();
        for (OrderColumn c : orderColumns) {
            // 构建新的排序规则
            OrderByElement orderByElement = new OrderByElement();
            // 设置排序 - 不传值则默认asc升序
            orderByElement.setAsc(SortRuleEnum.ASC == c.getSort());
            // 设置排序字段
            String columnName = String.valueOf(c.getOrderColumn());
            orderByElement.setExpression(new Column(columnName));
            // 重新封装条件 - 优先按自定义进行排序
            plainSelect.addOrderByElements(orderByElement);
        }
    }

    /**
     * 分页查询入参获取
     * 大概搜了下，没有到3个入参的，如果有，请将 PageDTO 放到第1或第2的位置
     * @param mpSh 处理对象
     * @return 分页查询入参
     * @author weiheng
     **/
    private PageModelNew getPageDTO(PluginUtils.MPStatementHandler mpSh) {
        PageModelNew pageModelNew = null;
        Object obj = mpSh.parameterHandler().getParameterObject();
        try {
            Object param = ((MapperMethod.ParamMap<?>) obj).get("param1");
            if (param instanceof PageModelNew p) {
                pageModelNew = p;
            } else {
                param = ((MapperMethod.ParamMap<?>) obj).get("param2");
                if (param instanceof PageModelNew p) {
                    pageModelNew = p;
                }
            }
        } catch (Exception e) {
            // 没有从SQL中获取到对应的参数（没有param1或param2），不走自定义分页逻辑
        }
        return pageModelNew;
    }

}